/*
 * @(#) $Header$
 *
 * Copyright (C) 2010  Daniel Léonard Forklabs
 */

package ca.forklabs.wow.net.realms;

import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import ca.forklabs.baselib.io.StreamUtil;
import ca.forklabs.wow.net.Region;

import java.io.IOException;

/**
 * Class {@code RealmList} is the repository of all the realms grouped by
 * regions.
 */
public class RealmList {
// TODO: make a dynamic realm list that populates and keeps itself updated with API calls
//---------------------------
// Instance variables
//---------------------------

   /** The maps of slugs to names indexed by regions. */
   private Map<Region, Map<String, String>> slugs2names = new HashMap<Region, Map<String, String>>();
   /** The maps of names to slugs indexed by regions. */
   private Map<Region, Map<String, String>> names2slugs = new HashMap<Region, Map<String, String>>();


//---------------------------
// Constructor
//---------------------------

   /**
    * Constructor.
    * @exception   IOException   if anything goes wrong while loading.
    */
   public RealmList() throws IOException {
      this.setup();
      }


//---------------------------
// Accessors and mutators
//---------------------------

   /**
    * Gets the index of slugs to names.
    * @return   the index of slugs to names.
    */
   protected Map<Region, Map<String, String>> getSlugsToNames() {
      return this.slugs2names;
      }

   /**
    * Gets the index of names to slugs.
    * @return   the index of names to slugs.
    */
   protected Map<Region, Map<String, String>> getNamesToSlugs() {
      return this.names2slugs;
      }


//---------------------------
// Instance methods
//---------------------------

   /**
    * Removes the realm translation if present.
    * @param   realm   the realm.
    * @return   a friendly realm.
    */
   protected String prepareRealm(String realm) {
      String pure = realm;

      if (null != pure) {
         int index = pure.indexOf(" ("); //$NON-NLS-1$
         if (-1 != index) {
            pure = pure.substring(0, index);
            }
         }

      return pure;
      }

   /**
    * Gets the slug corresponding to the given name.
    * @param   region   the region.
    * @param   name   the name.
    * @return   the slug.
    */
   public String getSlugFor(Region region, String name) {
      Map<Region, Map<String, String>> index = this.getNamesToSlugs();
      Map<String, String> realms = index.get(region);

      name = this.prepareRealm(name);

      String slug = realms.get(name);
      return slug;
      }

   /**
    * Gets the complete realm name with the translation corresponding to the
    * given slug.
    * @param   region   the region.
    * @param   slug   the slug.
    * @return   the complete name.
    */
   public String getNameFor(Region region, String slug) {
      Map<Region, Map<String, String>> index = this.getSlugsToNames();
      Map<String, String> realms = index.get(region);
      String name = realms.get(slug);
      return name;
      }


//---------------------------
// Setup instance methods
//---------------------------

   /**
    * Load a realm map. The realms are keyed by slugs and valued by name.
    * @param   resource   the resource path.
    * @return   a map of realms.
    * @exception   IOException   if anything goes wrong with I/O.
    */
   @SuppressWarnings("static-access")
   protected Map<String, String> loadRealms(String resource) throws IOException {
      InputStream is = RealmList.class.getResourceAsStream(resource);
      Charset charset = StreamUtil.UTF_8;

      String[] lines;
      try {
         lines = StreamUtil.readAllLines(is, charset);
         }
      finally {
         is.close();
         }

      Map<String, String> realms = new HashMap<String, String>();
      for (String line : lines) {
         int separator = line.indexOf('=');
         String key = line.substring(0, separator);
         String value = line.substring(separator + 1);
         realms.put(key, value);
         }
      return realms;
      }

   /**
    * Loads all the North American, Latina America, Brazil and Oceanic realms.
    * @return   all the realms.
    * @exception   IOException   if anything goes wrong with I/O.
    */
   protected Map<String, String> getAmericanRealms() throws IOException {
      String resource = "americas.list"; //$NON-NLS-1$
      Map<String, String> us_realms = this.loadRealms(resource);
      return us_realms;
      }

   /**
    * Loads all the European realms.
    * @return   all the realms.
    * @exception   IOException   if anything goes wrong with I/O.
    */
   protected Map<String, String> getEuropeRealms() throws IOException {
      String resource = "europe.list"; //$NON-NLS-1$
      Map<String, String> eu_realms = this.loadRealms(resource);
      return eu_realms;
      }

   /**
    * Loads all the Korean realms.
    * @return   all the realms.
    * @exception   IOException   if anything goes wrong with I/O.
    */
   protected Map<String, String> getKoreaRealms() throws IOException {
      String resource = "korea.list"; //$NON-NLS-1$
      Map<String, String> korean_realms = this.loadRealms(resource);
      return korean_realms;
      }

   /**
    * Loads all the Taiwanese realms.
    * @return   all the realms.
    * @exception   IOException   if anything goes wrong with I/O.
    */
   protected Map<String, String> getTaiwanRealms() throws IOException {
      String resource = "taiwan.list"; //$NON-NLS-1$
      Map<String, String> taiwan_realms = this.loadRealms(resource);
      return taiwan_realms;
      }

   /**
    * Loads all the Chinese realms.
    * @return   all the realms.
    * @exception   IOException   if anything goes wrong with I/O.
    */
   protected Map<String, String> getChinaRealms() throws IOException {
      String resource = "china.list"; //$NON-NLS-1$
      Map<String, String> cn_realms = this.loadRealms(resource);
      return cn_realms;
      }

   /**
    * Inverse a slugs to names map by creating a new map keyed by names. The
    * name key loose any translation they have in parenthesis.
    * @param   map  the realm map keyed by slug.
    * @return   a map keyed by name.
    * @see   #prepareRealm(String)
    */
   protected Map<String, String> inverse(Map<String, String> map) {
      Map<String, String> inverse = new HashMap<String, String>();

      Set<Map.Entry<String, String>> entries = map.entrySet();
      for (Map.Entry<String, String> entry : entries) {
         String slug = entry.getKey();
         String name = entry.getValue();

         name = this.prepareRealm(name);

         inverse.put(name, slug);
         }

      return inverse;
      }

   /**
    * Loads all the realms and setup the internal maps.
    * @exception   IOException   if anything goes wrong with I/O.
    */
   @SuppressWarnings("hiding")
   protected void setup() throws IOException {
      Map<String, String> american_realms = this.getAmericanRealms();
      Map<String, String> european_realms = this.getEuropeRealms();
      Map<String, String> korean_realms = this.getKoreaRealms();
      Map<String, String> taiwanese_realms = this.getTaiwanRealms();
      Map<String, String> chinese_realms = this.getChinaRealms();

      Map<Region, Map<String, String>> slugs2names = this.getSlugsToNames();
      slugs2names.put(Region.Americas, american_realms);
      slugs2names.put(Region.Europe, european_realms);
      slugs2names.put(Region.Korea, korean_realms);
      slugs2names.put(Region.Taiwan, taiwanese_realms);
      slugs2names.put(Region.China, chinese_realms);

      Map<Region, Map<String, String>> names2slugs = this.getNamesToSlugs();
      names2slugs.put(Region.Americas, this.inverse(american_realms));
      names2slugs.put(Region.Europe, this.inverse(european_realms));
      names2slugs.put(Region.Korea, this.inverse(korean_realms));
      names2slugs.put(Region.Taiwan, this.inverse(taiwanese_realms));
      names2slugs.put(Region.China, this.inverse(chinese_realms));
      }

   }
